﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using GalaSoft.MvvmLight;

namespace PhpDocBlockGenerator.Model
{
    public class Options : ViewModelBase, IDataErrorInfo
    {
        #region BoundProperties - an das Frontend gebundene Eigenschaften

        /// <summary>
        /// the private property for the source path
        /// </summary>
        private String _sourcePath;

        /// <summary>
        /// get/set for the source path
        /// </summary>
        public String SourcePath
        {
            get { return _sourcePath; }
            set
            {
                _sourcePath = value;
                RaisePropertyChanged("SourcePath");
                String[] parts = _sourcePath.Split(new[] { '\\' });
                RootNode = parts[parts.Length - 1];
            }
        }

        /// <summary>
        /// the private property for the target path
        /// </summary>
        private String _targetPath;

        /// <summary>
        /// get/set for the target path
        /// </summary>
        public String TargetPath
        {
            get { return _targetPath; }
            set { _targetPath = value; RaisePropertyChanged("TargetPath"); }
        }

        /// <summary>
        /// private property wether the files should be written to a new folder
        /// </summary>
        private bool _writeToNewFolder;

        /// <summary>
        /// get/set 
        /// </summary>
        public bool WriteToNewFolder
        {
            get { return _writeToNewFolder; }
            set { _writeToNewFolder = value; RaisePropertyChanged("WriteToNewFolder"); }
        }

        /// <summary>
        /// path to php.exe
        /// </summary>
        private String _phpPath;

        /// <summary>
        /// get/set for the php path
        /// </summary>
        public String PhpPath
        {
            get { return _phpPath; }
            set { _phpPath = value; RaisePropertyChanged("PhpPath"); }
        }

        /// <summary>
        /// private property wether the files should be validated with Php
        /// </summary>
        private bool _validateWithPhp;

        /// <summary>
        /// get/set for php validation
        /// </summary>
        public bool ValidateWithPhp
        {
            get { return _validateWithPhp; }
            set { _validateWithPhp = value; RaisePropertyChanged("ValidateWithPhp"); }
        }

        /// <summary>
        /// folder depth, which will be calculated
        /// </summary>
        private int _depth = 10;
        public int Depth
        {
            get { return _depth; }
            set { _depth = value; RaisePropertyChanged("Depth"); }
        }

        /// <summary>
        /// name of the root package
        /// </summary>
        private String _rootNode;
        public String RootNode
        {
            get { return _rootNode; }
            set { _rootNode = value; RaisePropertyChanged("RootNode"); }
        }

        /// <summary>
        /// specifies if the max Column Number is used or not
        /// </summary>
        private bool _isMaxColumnNumberUsed;
        public bool IsMaxColumnNumberUsed
        {
            get { return _isMaxColumnNumberUsed; }
            set { _isMaxColumnNumberUsed = value; RaisePropertyChanged("IsMaxColumnNumberUsed"); }
        }

        /// <summary>
        /// number of columns maximum
        /// </summary>
        private int _maxColumnNumber = 120;
        public int MaxColumnNumber
        {
            get { return _maxColumnNumber; }
            set { _maxColumnNumber = value; RaisePropertyChanged("MaxColumnNumber"); }
        }

        /// <summary>
        /// indicates whether the process isn't active or not
        /// </summary>
        private bool _isIdle = true;
        [XmlIgnore]
        public bool IsIdle
        {
            get { return _isIdle; }
            set { _isIdle = value; RaisePropertyChanged("IsIdle");}
        }

        #endregion

        #region FilesWithErrors - list containing of files with errors including threadlock
        /// <summary>
        /// contains a list of all files, which have a parse error after
        /// inserting the docblocks
        /// </summary>
        private List<String> _filesWithErrors = new List<String>();
        private Semaphore _filesWithErrorsSema = new Semaphore(1, 1);
        [XmlIgnore]
        public List<String> FilesWithErrors
        {
            get
            {
                return _filesWithErrors;
            }
            set
            {
                _filesWithErrorsSema.WaitOne();
                _filesWithErrors = value;
                _filesWithErrorsSema.Release();
            }
        }
        public void AddFileToFilesWithErrors(String file)
        {
            _filesWithErrorsSema.WaitOne();
            _filesWithErrors.Add(file);
            _filesWithErrorsSema.Release();
        }
        public void ClearFilesWithErrors()
        {
            _filesWithErrorsSema.WaitOne();
            _filesWithErrors.Clear();
            _filesWithErrorsSema.Release();
        }

        #endregion

        /// <summary>
        /// all folders which contain php files
        /// </summary>
        private List<String> _foldersWithPhpFiles = new List<String>();
        [XmlIgnore]
        public List<String> FoldersWithPhpFiles
        {
            get { return _foldersWithPhpFiles; }
            set { _foldersWithPhpFiles = value; }
        }

        /// <summary>
        /// the numbers of total php files
        /// </summary>
        [XmlIgnore]
        public int PhpFileCount { get; set; }

        #region CurrentProgress - represents the progress of the complete operation
        /// <summary>
        /// current progress state of parsing and writing files
        /// </summary>
        private double _currentProgress;
        private Semaphore _currentProgressSema = new Semaphore(1, 1);
        private Semaphore _currentProgressUpdateSema = new Semaphore(1, 1);

        public Options()
        {
            PhpFileCount = 0;
        }


        [XmlIgnore]
        public double CurrentProgress
        {
            get
            {
                //should not need to be locked (due to atomar operation)
                return _currentProgress;
            }

            set
            {
                //lock value, write it and unlock
                //(should not need to be locked due to atomar operation)
                _currentProgressSema.WaitOne();
                _currentProgress = value;
                RaisePropertyChanged("CurrentProgress");
                Debug.WriteLine("Sets the Progress to " + value);
                _currentProgressSema.Release();
            }
        }

        /// <summary>
        /// updates the current progress state with the value of 1 file processed
        /// </summary>
        public void UpdateProgressWithOneFile()
        {
            //not atomic -> need to lock the operations
            //lock value, write it and unlock
            //(should not need to be locked due to atomar operation)
            _currentProgressUpdateSema.WaitOne();
            CurrentProgress = CurrentProgress + (100.0 / PhpFileCount);
            _currentProgressUpdateSema.Release();
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        [XmlIgnore]
        public string this[string columnName]
        {
            get 
            { 
                String result = String.Empty;
                switch (columnName)
                {
                    case "MaxColumnNumber":

                        break;
                }
                return result;
            }
        }

        [XmlIgnore]
        public string Error { get; set; }


        /// <summary>
        /// Saves the Option Container to a file
        /// </summary>
        /// <param name="options"></param>
        public static void Write(Options options)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Options));
            TextWriter textWriter = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + "settings.xml");
            serializer.Serialize(textWriter, options);
            textWriter.Close();
        }

        /// <summary>
        /// Loads an Option Container
        /// </summary>
        /// <param name="?"></param>
        public static Options GetOptions()
        {
            Options options = null;
            if (File.Exists(AppDomain.CurrentDomain.BaseDirectory + "settings.xml"))
            {
                try
                {
                    XmlSerializer deserializer = new XmlSerializer(typeof (Options));
                    TextReader textReader = new StreamReader(AppDomain.CurrentDomain.BaseDirectory + "settings.xml");
                    options = (Options) deserializer.Deserialize(textReader);
                    textReader.Close();
                }
                catch (Exception e)
                {
                    // nothing to do here
                }
            }
            if (options == null)
            {
                options = new Options();
            }

            return options;
        }
    }
}
